import logging
from constants import UUID_NIL
from core.entities.account_role import AccountWrap
from extensions.ext_database import db
from models.role import SysMenu,SysMenuType
from flask_babel import _
from services.errors.roles import SysMenuNotFoundError, SysMenuParamError
class SysMenuService:
    @staticmethod
    def get_menu_list(tenant_id:str,menu_id:str|None,parent_id:str|None,name:str|None,menu_type:str|None,page:int=-1,page_size:int=-1) -> dict:
        try:
            query = db.session.query(SysMenu).filter(SysMenu.tenant_id==tenant_id)
            total = 0
            has_more = False
            if menu_id:
                query = query.filter(SysMenu.id==menu_id)
                total = query.count()
            else:
                if parent_id:
                    query = query.filter(SysMenu.parent_id==parent_id)
                if name:
                    query = query.filter(SysMenu.name.ilike(f"%{name}%"))
                if menu_type and menu_type in SysMenuType.choices():
                    query = query.filter(SysMenu.menu_type==menu_type)
                query = query.order_by(SysMenu.seq.asc())
                total = query.count()
                if page and page_size and page_size >0 and page >0:
                    query = query.offset((page-1)*page_size).limit(page_size)
                    has_more = total > page*page_size
            data = query.all()
            return {"total":total, "has_more":has_more, "data":data}
        except Exception as e:
            logging.error(e)
        return {"total":0, "has_more":False, "data":[]}
    
    @staticmethod
    def create_menu(account:AccountWrap,args:dict)->SysMenu:
        parent_id = args.get('parent_id')
        if parent_id and parent_id != UUID_NIL:
            parent = db.session.get(SysMenu,parent_id)
            if not parent:
                raise SysMenuNotFoundError(parent_id)
        name = args.get('name')
        url = args.get('url')
        perms = args.get('perms')
        menu_type = args.get('menu_type')
        icon = args.get('icon')
        component = args.get('component')
        # icon_url = args.get('icon_url')
        # seq = args.get('seq')
        if not name:
            raise SysMenuParamError(_("Menu name cannot be empty"))
        # if icon_file_id and icon_url:
        #     raise SysMenuParamError(_("Only one icon can be selected"))
        new_seq = 1
        if parent_id:
            new_seq = db.session.query(SysMenu.seq).filter(
                SysMenu.parent_id == parent_id,
                SysMenu.tenant_id == account.current_tenant_id
            ).order_by(SysMenu.seq.desc()).first()
            if new_seq:
                new_seq = new_seq[0] + 1
            else:
                new_seq = 1
        try:
            menu = SysMenu(
                tenant_id=account.current_tenant_id,# type:ignore
                parent_id=parent_id,# type:ignore
                name=name,# type:ignore
                url = url,# type:ignore
                perms=perms,# type:ignore
                menu_type=menu_type,# type:ignore
                icon=icon,# type:ignore
                # icon_url=icon_url,# type:ignore
                component = component,# type:ignore
                seq=new_seq,# type:ignore
                created_by=account.id,# type:ignore
            )
            db.session.add(menu)
            db.session.commit()
            return menu
        except Exception as e:
            db.session.rollback()
            raise e
            
    @staticmethod
    def reorder_menu(tenant_id:str,parent_id:str|None,submit:bool=False):
        menus = []
        if parent_id:
            menus = db.session.query(SysMenu).filter(
                SysMenu.tenant_id==tenant_id,
                SysMenu.parent_id==parent_id,
            ).order_by(SysMenu.seq.asc())
        else:
            menus = db.session.query(SysMenu).filter(
                SysMenu.tenant_id==tenant_id,
                SysMenu.parent_id==None,
            ).order_by(SysMenu.seq.asc())
        i = 1
        for menu in menus:
            menu.seq = i
            i += 1
        
    @classmethod
    def update_menu(cls,account:AccountWrap,args:dict)->SysMenu:
        menu_id = args.get('id',"")
        menu = db.session.get(SysMenu,menu_id)
        if not menu:
            raise SysMenuNotFoundError(menu_id)
        parent_id = args.get('parent_id')
        if parent_id:
            parent_menu = db.session.get(SysMenu,parent_id)
            if not parent_menu and parent_id != UUID_NIL:
                raise SysMenuNotFoundError(parent_id)
            menu.parent_id = parent_id
        name = args.get('name')
        url = args.get('url')
        perms = args.get('perms')
        menu_type = args.get('menu_type')
        icon = args.get('icon')
        component = args.get('component')
        # icon_url = args.get('icon_url')
        seq = args.get('seq')
        if not name:
            raise SysMenuParamError(_("Menu name cannot be empty"))
        # if icon and icon_url:
        #     raise SysMenuParamError(_("Only one icon can be selected"))
        try:
            menu.name = name
            menu.url = url
            menu.perms = perms
            menu.menu_type = menu_type
            menu.icon = icon
            menu.component = component
            if seq:
                menu.seq = int(seq)
            cls.reorder_menu(account.current_tenant_id,parent_id)
            db.session.commit()
            return menu
        except Exception as e:
            db.session.rollback()
            raise e
    
    @staticmethod
    def delete_menu(tenant_id:str,menu_id:str|None):
        if not menu_id:
            raise SysMenuParamError(_("The menu id is required."))
        menu = db.session.get(SysMenu,menu_id)
        if not menu:
            raise SysMenuNotFoundError(menu_id)
        if menu.tenant_id != tenant_id:
            raise SysMenuNotFoundError(menu_id)
        if menu.is_inner:
            raise SysMenuParamError(_("Cannot delete built-in menu"))
        childs = db.session.query(SysMenu).filter(SysMenu.parent_id==menu_id).all()
        try:
            db.session.delete(menu)
            for child in childs:
                db.session.delete(child)
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e
